/**
 * @file
 * @name UM.Utils
 * @short Utils
 * @desc UEditor封装使用的静态工具函数
 * @import editor.js
 */
var utils = (UM.utils = {
  /**
   * 遍历数组，对象，nodeList
   * @name each
   * @grammar UM.utils.each(obj,iterator,[context])
   * @since 1.2.4+
   * @desc
   * * obj 要遍历的对象
   * * iterator 遍历的方法,方法的第一个是遍历的值，第二个是索引，第三个是obj
   * * context  iterator的上下文
   * @example
   * UM.utils.each([1,2],function(v,i){
   *     console.log(v)//值
   *     console.log(i)//索引
   * })
   * UM.utils.each(document.getElementsByTagName('*'),function(n){
   *     console.log(n.tagName)
   * })
   */
  each: function (obj, iterator, context) {
    if (obj == null) return;
    if (obj.length === +obj.length) {
      for (var i = 0, l = obj.length; i < l; i++) {
        if (iterator.call(context, obj[i], i, obj) === false) return false;
      }
    } else {
      for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
          if (iterator.call(context, obj[key], key, obj) === false) return false;
        }
      }
    }
  },

  makeInstance: function (obj) {
    var noop = new Function();
    noop.prototype = obj;
    obj = new noop();
    noop.prototype = null;
    return obj;
  },
  /**
   * 将source对象中的属性扩展到target对象上
   * @name extend
   * @grammar UM.utils.extend(target,source)  => Object  //覆盖扩展
   * @grammar UM.utils.extend(target,source,true)  ==> Object  //保留扩展
   */
  extend: function (t, s, b) {
    if (s) {
      for (var k in s) {
        if (!b || !t.hasOwnProperty(k)) {
          t[k] = s[k];
        }
      }
    }
    return t;
  },
  extend2: function (t) {
    var a = arguments;
    for (var i = 1; i < a.length; i++) {
      var x = a[i];
      for (var k in x) {
        if (!t.hasOwnProperty(k)) {
          t[k] = x[k];
        }
      }
    }
    return t;
  },
  /**
   * 模拟继承机制，subClass继承superClass
   * @name inherits
   * @grammar UM.utils.inherits(subClass,superClass) => subClass
   * @example
   * function SuperClass(){
   *     this.name = "小李";
   * }
   * SuperClass.prototype = {
   *     hello:function(str){
   *         console.log(this.name + str);
   *     }
   * }
   * function SubClass(){
   *     this.name = "小张";
   * }
   * UM.utils.inherits(SubClass,SuperClass);
   * var sub = new SubClass();
   * sub.hello("早上好!"); ==> "小张早上好！"
   */
  inherits: function (subClass, superClass) {
    var oldP = subClass.prototype,
      newP = utils.makeInstance(superClass.prototype);
    utils.extend(newP, oldP, true);
    subClass.prototype = newP;
    return (newP.constructor = subClass);
  },

  /**
   * 用指定的context作为fn上下文，也就是this
   * @name bind
   * @grammar UM.utils.bind(fn,context)  =>  fn
   */
  bind: function (fn, context) {
    return function () {
      return fn.apply(context, arguments);
    };
  },

  /**
   * 创建延迟delay执行的函数fn
   * @name defer
   * @grammar UM.utils.defer(fn,delay)  =>fn   //延迟delay毫秒执行fn，返回fn
   * @grammar UM.utils.defer(fn,delay,exclusion)  =>fn   //延迟delay毫秒执行fn，若exclusion为真，则互斥执行fn
   * @example
   * function test(){
   *     console.log("延迟输出！");
   * }
   * //非互斥延迟执行
   * var testDefer = UM.utils.defer(test,1000);
   * testDefer();   =>  "延迟输出！";
   * testDefer();   =>  "延迟输出！";
   * //互斥延迟执行
   * var testDefer1 = UM.utils.defer(test,1000,true);
   * testDefer1();   =>  //本次不执行
   * testDefer1();   =>  "延迟输出！";
   */
  defer: function (fn, delay, exclusion) {
    var timerID;
    return function () {
      if (exclusion) {
        clearTimeout(timerID);
      }
      timerID = setTimeout(fn, delay);
    };
  },

  /**
   * 查找元素item在数组array中的索引, 若找不到返回-1
   * @name indexOf
   * @grammar UM.utils.indexOf(array,item)  => index|-1  //默认从数组开头部开始搜索
   * @grammar UM.utils.indexOf(array,item,start)  => index|-1  //start指定开始查找的位置
   */
  indexOf: function (array, item, start) {
    var index = -1;
    start = this.isNumber(start) ? start : 0;
    this.each(array, function (v, i) {
      if (i >= start && v === item) {
        index = i;
        return false;
      }
    });
    return index;
  },

  /**
   * 移除数组array中的元素item
   * @name removeItem
   * @grammar UM.utils.removeItem(array,item)
   */
  removeItem: function (array, item) {
    for (var i = 0, l = array.length; i < l; i++) {
      if (array[i] === item) {
        array.splice(i, 1);
        i--;
      }
    }
  },

  /**
   * 删除字符串str的首尾空格
   * @name trim
   * @grammar UM.utils.trim(str) => String
   */
  trim: function (str) {
    return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, "");
  },

  /**
   * 将字符串list(以','分隔)或者数组list转成哈希对象
   * @name listToMap
   * @grammar UM.utils.listToMap(list)  => Object  //Object形如{test:1,br:1,textarea:1}
   */
  listToMap: function (list) {
    if (!list) return {};
    list = utils.isArray(list) ? list : list.split(",");
    for (var i = 0, ci, obj = {}; (ci = list[i++]); ) {
      obj[ci.toUpperCase()] = obj[ci] = 1;
    }
    return obj;
  },

  /**
   * 将str中的html符号转义,默认将转义''&<">''四个字符，可自定义reg来确定需要转义的字符
   * @name unhtml
   * @grammar UM.utils.unhtml(str);  => String
   * @grammar UM.utils.unhtml(str,reg)  => String
   * @example
   * var html = '<body>You say:"你好！Baidu & UEditor!"</body>';
   * UM.utils.unhtml(html);   ==>  &lt;body&gt;You say:&quot;你好！Baidu &amp; UEditor!&quot;&lt;/body&gt;
   * UM.utils.unhtml(html,/[<>]/g)  ==>  &lt;body&gt;You say:"你好！Baidu & UEditor!"&lt;/body&gt;
   */
  unhtml: function (str, reg) {
    return str
      ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g, function (a, b) {
          if (b) {
            return a;
          } else {
            return {
              "<": "&lt;",
              "&": "&amp;",
              '"': "&quot;",
              ">": "&gt;",
              "'": "&#39;",
            }[a];
          }
        })
      : "";
  },
  /**
   * 将str中的转义字符还原成html字符
   * @name html
   * @grammar UM.utils.html(str)  => String   //详细参见<code><a href = '#unhtml'>unhtml</a></code>
   */
  html: function (str) {
    return str
      ? str.replace(/&((g|l|quo)t|amp|#39);/g, function (m) {
          return {
            "&lt;": "<",
            "&amp;": "&",
            "&quot;": '"',
            "&gt;": ">",
            "&#39;": "'",
          }[m];
        })
      : "";
  },
  /**
   * 将css样式转换为驼峰的形式。如font-size => fontSize
   * @name cssStyleToDomStyle
   * @grammar UM.utils.cssStyleToDomStyle(cssName)  => String
   */
  cssStyleToDomStyle: (function () {
    var test = document.createElement("div").style,
      cache = {
        float: test.cssFloat != undefined ? "cssFloat" : test.styleFloat != undefined ? "styleFloat" : "float",
      };

    return function (cssName) {
      return (
        cache[cssName] ||
        (cache[cssName] = cssName.toLowerCase().replace(/-./g, function (match) {
          return match.charAt(1).toUpperCase();
        }))
      );
    };
  })(),
  /**
   * 动态加载文件到doc中，并依据obj来设置属性，加载成功后执行回调函数fn
   * @name loadFile
   * @grammar UM.utils.loadFile(doc,obj)
   * @grammar UM.utils.loadFile(doc,obj,fn)
   * @example
   * //指定加载到当前document中一个script文件，加载成功后执行function
   * utils.loadFile( document, {
   *     src:"test.js",
   *     tag:"script",
   *     type:"text/javascript",
   *     defer:"defer"
   * }, function () {
   *     console.log('加载成功！')
   * });
   */
  loadFile: (function () {
    var tmpList = [];

    function getItem(doc, obj) {
      try {
        for (var i = 0, ci; (ci = tmpList[i++]); ) {
          if (ci.doc === doc && ci.url == (obj.src || obj.href)) {
            return ci;
          }
        }
      } catch (e) {
        return null;
      }
    }

    return function (doc, obj, fn) {
      var item = getItem(doc, obj);
      if (item) {
        if (item.ready) {
          fn && fn();
        } else {
          item.funs.push(fn);
        }
        return;
      }
      tmpList.push({
        doc: doc,
        url: obj.src || obj.href,
        funs: [fn],
      });
      if (!doc.body) {
        var html = [];
        for (var p in obj) {
          if (p == "tag") continue;
          html.push(p + '="' + obj[p] + '"');
        }
        doc.write("<" + obj.tag + " " + html.join(" ") + " ></" + obj.tag + ">");
        return;
      }
      if (obj.id && doc.getElementById(obj.id)) {
        return;
      }
      var element = doc.createElement(obj.tag);
      delete obj.tag;
      for (var p in obj) {
        element.setAttribute(p, obj[p]);
      }
      element.onload = element.onreadystatechange = function () {
        if (!this.readyState || /loaded|complete/.test(this.readyState)) {
          item = getItem(doc, obj);
          if (item.funs.length > 0) {
            item.ready = 1;
            for (var fi; (fi = item.funs.pop()); ) {
              fi();
            }
          }
          element.onload = element.onreadystatechange = null;
        }
      };
      element.onerror = function () {
        throw Error("The load " + (obj.href || obj.src) + " fails,check the url settings of file umeditor.config.js ");
      };
      doc.getElementsByTagName("head")[0].appendChild(element);
    };
  })(),
  /**
   * 判断obj对象是否为空
   * @name isEmptyObject
   * @grammar UM.utils.isEmptyObject(obj)  => true|false
   * @example
   * UM.utils.isEmptyObject({}) ==>true
   * UM.utils.isEmptyObject([]) ==>true
   * UM.utils.isEmptyObject("") ==>true
   */
  isEmptyObject: function (obj) {
    if (obj == null) return true;
    if (this.isArray(obj) || this.isString(obj)) return obj.length === 0;
    for (var key in obj) if (obj.hasOwnProperty(key)) return false;
    return true;
  },

  /**
   * 统一将颜色值使用16进制形式表示
   * @name fixColor
   * @grammar UM.utils.fixColor(name,value) => value
   * @example
   * rgb(255,255,255)  => "#ffffff"
   */
  fixColor: function (name, value) {
    if (/color/i.test(name) && /rgba?/.test(value)) {
      var array = value.split(",");
      if (array.length > 3) return "";
      value = "#";
      for (var i = 0, color; (color = array[i++]); ) {
        color = parseInt(color.replace(/[^\d]/gi, ""), 10).toString(16);
        value += color.length == 1 ? "0" + color : color;
      }
      value = value.toUpperCase();
    }
    return value;
  },

  /**
   * 深度克隆对象，从source到target
   * @name clone
   * @grammar UM.utils.clone(source) => anthorObj 新的对象是完整的source的副本
   * @grammar UM.utils.clone(source,target) => target包含了source的所有内容，重名会覆盖
   */
  clone: function (source, target) {
    var tmp;
    target = target || {};
    for (var i in source) {
      if (source.hasOwnProperty(i)) {
        tmp = source[i];
        if (typeof tmp == "object") {
          target[i] = utils.isArray(tmp) ? [] : {};
          utils.clone(source[i], target[i]);
        } else {
          target[i] = tmp;
        }
      }
    }
    return target;
  },
  /**
   * 转换cm/pt到px
   * @name transUnitToPx
   * @grammar UM.utils.transUnitToPx('20pt') => '27px'
   * @grammar UM.utils.transUnitToPx('0pt') => '0'
   */
  transUnitToPx: function (val) {
    if (!/(pt|cm)/.test(val)) {
      return val;
    }
    var unit;
    val.replace(/([\d.]+)(\w+)/, function (str, v, u) {
      val = v;
      unit = u;
    });
    switch (unit) {
      case "cm":
        val = parseFloat(val) * 25;
        break;
      case "pt":
        val = Math.round((parseFloat(val) * 96) / 72);
    }
    return val + (val ? "px" : "");
  },
  /**
   * 动态添加css样式
   * @name cssRule
   * @grammar UM.utils.cssRule('添加的样式的节点名称',['样式'，'放到哪个document上'])
   * @grammar UM.utils.cssRule('body','body{background:#ccc}') => null  //给body添加背景颜色
   * @grammar UM.utils.cssRule('body') =>样式的字符串  //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空，例如刚才那个背景颜色，将返回 body{background:#ccc}
   * @grammar UM.utils.cssRule('body','') =>null //清空给定的key值的背景颜色
   */
  cssRule:
    browser.ie && browser.version != 11
      ? function (key, style, doc) {
          var indexList, index;
          doc = doc || document;
          if (doc.indexList) {
            indexList = doc.indexList;
          } else {
            indexList = doc.indexList = {};
          }
          var sheetStyle;
          if (!indexList[key]) {
            if (style === undefined) {
              return "";
            }
            sheetStyle = doc.createStyleSheet("", (index = doc.styleSheets.length));
            indexList[key] = index;
          } else {
            sheetStyle = doc.styleSheets[indexList[key]];
          }
          if (style === undefined) {
            return sheetStyle.cssText;
          }
          sheetStyle.cssText = style || "";
        }
      : function (key, style, doc) {
          doc = doc || document;
          var head = doc.getElementsByTagName("head")[0],
            node;
          if (!(node = doc.getElementById(key))) {
            if (style === undefined) {
              return "";
            }
            node = doc.createElement("style");
            node.id = key;
            head.appendChild(node);
          }
          if (style === undefined) {
            return node.innerHTML;
          }
          if (style !== "") {
            node.innerHTML = style;
          } else {
            head.removeChild(node);
          }
        },
});
/**
 * 判断str是否为字符串
 * @name isString
 * @grammar UM.utils.isString(str) => true|false
 */
/**
 * 判断array是否为数组
 * @name isArray
 * @grammar UM.utils.isArray(obj) => true|false
 */
/**
 * 判断obj对象是否为方法
 * @name isFunction
 * @grammar UM.utils.isFunction(obj)  => true|false
 */
/**
 * 判断obj对象是否为数字
 * @name isNumber
 * @grammar UM.utils.isNumber(obj)  => true|false
 */
utils.each(["String", "Function", "Array", "Number", "RegExp", "Object"], function (v) {
  UM.utils["is" + v] = function (obj) {
    return Object.prototype.toString.apply(obj) == "[object " + v + "]";
  };
});
